package com.sxt.aspect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.sxt.annos.DeleteCache;
import com.sxt.annos.StartCache;
import com.sxt.domain.User;
import com.sxt.utils.BloomFiler;
import com.sxt.utils.SerializableUtil;

import redis.clients.jedis.Jedis;

/**
 * 控制缓存的切面类
 * @author Liushuangxi
 *
 */
@Component
@Aspect
public class CacheAspect {
	@Autowired
	private Jedis jedis;

	/**
	 * 开启缓存
	 * @param point
	 * @return
	 * @Around("execution (* com.sxt.service.impl.*.save(..))")
	 */
	@Around("@annotation(com.sxt.annos.StartCache)")
	public Object cache(ProceedingJoinPoint point) {
		// 得到缓存里面的key
		String key = getKey(point);
		//先判断数据的key是否存在bitset集合中
		if(!BloomFiler.existInBitSet(key)) {
			return null;
		}
		// 判断redis里面是否存在key
		if (jedis.exists(key.getBytes())) {
			System.out.println("***********在redis里面获取数据***********");
			// 取出redis里面key对应的字节流
			byte[] bs = jedis.get(key.getBytes());
			// 将取出的字节流转化对象
			return SerializableUtil.deSerializable(bs);
		}
		
		//缓存里面没有，执行查询数据库的方法
		Object object = null;
		try {
			// 执行方法
			System.out.println("在mysql数据库里面获取数据---->>>>>>>>>>>>>");
			object = point.proceed(point.getArgs());
		} catch (Throwable e) {
			e.printStackTrace();
		}
		// 手动将数据库数据存入到缓存里面
		jedis.set(key.getBytes(), SerializableUtil.serializable(object));
		System.out.println("数据已经写入redis>>>>>>>>>>>>");
		BloomFiler.save2BitSet(key);
		System.out.println("数据的"+key+"已存入bitset集合中");
		return object;
	}

	@Around("execution (* com.sxt.service.impl.*.add(..))")
	public Object savaToBitSet(ProceedingJoinPoint point) {
		// 保存成功后，将数据保存在mysql->Redis->BitSet
		Object object = null;
		try {
			// 执行方法，将数据存入数据库
			object = point.proceed(point.getArgs());
			System.out.println("用户已经存入数据库");
			// 得到属性名
			String className = object.getClass().getName();
			// 得到id的属性
			Field field = object.getClass().getDeclaredField("id");
			field.setAccessible(true);
			// 得到id的属性值
			Object id = field.get(object);
			//构造数据存入缓存的key
			String key = className+":"+String.valueOf(id);
			// 将数据存入缓存
			jedis.set(key.getBytes(),SerializableUtil.serializable(object));
			System.out.println("数据已存入缓存");
			// 将数据的key存入bitset集合
			BloomFiler.save2BitSet(key);
			System.out.println("数据的id已经存入bitset集合");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return point;
	}
	/*
	 *得到key 
	 * @param point
	 * @return
	 
	private String getKey0(ProceedingJoinPoint point) {
		StringBuilder sb = new StringBuilder();
		// 获取类的名称,并追加到Stringbild里面
		String className = point.getTarget().getClass().getName();
		sb.append(className).append(".");
		// 获取方法签名
		MethodSignature signature = (MethodSignature) point.getSignature();
		// 获取方法
		Method method = signature.getMethod();
		// 获取方法名称
		String methodName = method.getName();
		// 获取方法的名称,并追加到Stringbild里面
		sb.append(methodName).append(":");
		//获取参数的hash值
		Object[] args = point.getArgs();
		for (Object object : args) {
			int hashCode = object.hashCode();
			sb.append(hashCode+"-");
		}
		if(sb.lastIndexOf("-")==sb.length()-1) { //若最后一个是-，则删除
			sb.deleteCharAt(sb.length()-1);
		}
		return sb.toString();
	}*/
	

	/**
	 * 通过el解析得到方法的类型以及方法参数，并生成key
	 * @param point
	 * @return
	 */
	private String getKey(ProceedingJoinPoint point) {
		// 得到当前执行方法的方法签名
		MethodSignature methodSignature = (MethodSignature) point.getSignature();
		// 通过方法签名得到对应的方法
		Method method = methodSignature.getMethod();
		// 得到方法对应的注解
		StartCache startCache = method.getAnnotation(StartCache.class);
		DeleteCache deleteCache = method.getAnnotation(DeleteCache.class);
		// 实例Stringbuild来追加字符串
		StringBuilder sb = new StringBuilder();
		//得到注解的参数,并判断是否开启注解
		String prefix= null;
		String params = null;
		if(null!=startCache) {
			prefix = startCache.prefix();
			params = startCache.params();
		}
		if(null!= deleteCache) {
			prefix = deleteCache.prefix();
			params = deleteCache.params();
		}
		// 通过el解析得到方法参数
		String parseExpressions = parseExpressions(params,point.getArgs());
		sb.append(prefix).append(":").append(parseExpressions);
		return sb.toString();
	}
	
	
	/*
	 * el 表达式的解析
	 * #0 第一个参数
	 * #0.id第一个对象里面的id 实现值
	 *  其余的都是字符串
	 * @param args
	 * el 本质：取方法参数里的值
	 */
	public static String parseExpressions(String expression,Object[] args) {
		if(null==expression) throw new RuntimeException("表达式不能为null值");
		//实例化stringbuilder追加得到的参数字符串
		StringBuilder sb = new StringBuilder();
		// 分割expression
		String[] exressValues = expression.split("#");
		// 遍历对象属性值
		for (int i = 1; i < exressValues.length; i++) {
			if (exressValues[i].contains(".")) {
				// 继续截取exressValues[i],存在0.id类似的属性
				String[] objectValue = exressValues[i].split("\\.");
				// 得到对象的索引
				String objectIndex = objectValue[0];
				// 得到属性的名称
				String propName = objectValue[1];
				// 得到对象
				Object object = args[Integer.valueOf(objectIndex)];
				String value = null;
				try {
					// 得到属性名称
					Field field = object.getClass().getDeclaredField(propName);
					field.setAccessible(true);
					// 得到属性值
					value = String.valueOf(field.get(object));
					// 将值追加给字符串stringbuild
					sb.append(value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}else {
				// 得到属性值
				Object value = args[Integer.valueOf(exressValues[i])];
				sb.append(value.toString());
			}
			sb.append("-");
		}
		sb.deleteCharAt(sb.lastIndexOf("-"));
		return sb.toString();
	}
	
	
	@Around("@annotation(com.sxt.annos.DeleteCache)")
	public Object deleteCache(ProceedingJoinPoint point) {
		// 得到删除对象的key
		String key = getKey(point);
		//判断redis里面是否存在该key，存在则删除
		if (jedis.exists(key.getBytes())) {
			System.out.println("将要删除Redis里面: "+key+" 的数据");
			jedis.del(key.getBytes());
		}
		Object object = null;
		try {
			//执行真实方法，删除数据库对象
			System.out.println("将要删除数据库里面的数据");
			object = point.proceed(point.getArgs());
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return object;
	}
	
	public static void main(String[] args) {
		Object[] objs = new Object[2];
		User user = new User();
		user.setId(1);
		objs[0] = user;
		// 若参数有多个，则中间使用“-” 分割
		String parseExpression = parseExpressions("#0.id", objs);
		System.out.println(parseExpression);
	}
}
